package com.zxw.user.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zxw.common.core.base.constant.GlobalConstants;
import com.zxw.common.core.base.constant.RedisConstants;
import com.zxw.common.core.base.enums.ResponseEnum;
import com.zxw.common.core.exception.AppException;
import com.zxw.common.web.annotation.InitInsertInfo;
import com.zxw.common.web.annotation.InitSelectInfo;
import com.zxw.common.web.annotation.InitUpdateInfo;
import com.zxw.common.web.util.JwtUtils;
import com.zxw.flow.client.toc.FilePathClient;
import com.zxw.user.domain.dto.UserDTO;
import com.zxw.user.domain.dto.UserUpdateDTO;
import com.zxw.user.domain.entity.User;
import com.zxw.user.domain.entity.UserRole;
import com.zxw.user.domain.vo.*;
import com.zxw.user.mapper.UserMapper;
import com.zxw.user.service.EmailService;
import com.zxw.user.service.UserRoleService;
import com.zxw.user.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author Xuewei.Zhang
 * @since 2022-01-21
 */
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private final UserMapper userMapper;

    private final UserRoleService userRoleService;

    private final PasswordEncoder passwordEncoder;

    private final FilePathClient filePathClient;

    private final StringRedisTemplate stringRedisTemplate;

    private final EmailService emailService;

    @Override
    @InitSelectInfo
    public IPage<UserDTO> getUserList(UserPageVO pageVO) {
        Page<UserDTO> userDTOPage = new Page<>(pageVO.getPageNum(), pageVO.getPageSize());
        List<UserDTO> userDTOList = userMapper.getUserPage(userDTOPage, pageVO);
        userDTOPage.setRecords(userDTOList);
        return userDTOPage;
    }

    @Override
    @InitInsertInfo
    public User add(UserAddVO userAddVO) {
        List<Long> roleIds = userAddVO.getRoleIds();
        User user = new User();
        BeanUtils.copyProperties(userAddVO, user);
        user.setPasswordCode(passwordEncoder.encode(GlobalConstants.DEFAULT_USER_PASSWORD));
        userMapper.insert(user);
        List<UserRole> userRoles = new ArrayList<>();
        for (int i = 0; i < roleIds.size(); i++) {
            UserRole userRole = new UserRole(user);
            userRole.setRoleId(roleIds.get(i));
            userRoles.add(userRole);
        }
        userRoleService.saveBatch(userRoles);
        return user;
    }

    @Override
    public UserUpdateDTO getUserById(Long userId) {
        Long companyId = JwtUtils.getCompanyId();
        User user = userMapper.getOneByIdAndCompanyId(userId, companyId);
        UserUpdateDTO userUpdateDTO = new UserUpdateDTO();
        BeanUtils.copyProperties(user, userUpdateDTO);
        List<Long> roleIds = userRoleService.getRoleIds(userId);
        userUpdateDTO.setRoleIds(roleIds);
        return userUpdateDTO;
    }

    @Override
    @InitUpdateInfo
    public void updateBase(UserUpdateVO userUpdateVO) {
        Long companyId = JwtUtils.getCompanyId();
        Long userId = userUpdateVO.getId();
        // 获取用户的角色数据
        List<Long> roleIds = userRoleService.getRoleIds(userId);

        List<Long> upRoleIds = userUpdateVO.getRoleIds();

        List<Long> roleIdsSorted = roleIds.stream().sorted().collect(Collectors.toList());
        List<Long> roleIdsUpSorted = upRoleIds.stream().sorted().collect(Collectors.toList());

        User user = new User();
        BeanUtils.copyProperties(userUpdateVO, user);
        userMapper.update(user, new LambdaQueryWrapper<User>()
                .eq(User::getCompanyId, companyId)
                .eq(User::getId, userId)
        );

        // 删除的移除的角色
        List<Long> delRoleIds = roleIdsSorted.stream().filter(item -> !roleIdsUpSorted.contains(item)).collect(Collectors.toList());
        if (delRoleIds.size() > 0)
            userRoleService.deleteByUserIdAndRoleIds(userId, delRoleIds);
        List<Long> addRoleIds = roleIdsUpSorted.stream().filter(item -> !roleIdsSorted.contains(item)).collect(Collectors.toList());
        if (addRoleIds.size() > 0)
            userRoleService.insertBatch(userId, addRoleIds);
    }

    @Override
    public Boolean delete(String userIds) {
        Long companyId = JwtUtils.getCompanyId();
        userRoleService.deleteByUserIds(userIds);
        return this.remove(new LambdaQueryWrapper<User>()
                .eq(User::getCompanyId, companyId)
                .in(User::getId, Arrays.asList(userIds.split(",")))
        );
    }


    /**
     * 更新部分信息：密码、状态
     *
     * @param user
     */
    @Override
    @InitUpdateInfo
    public void updatePart(User user) {
        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<User>()
                .eq(User::getCompanyId, user.getCompanyId())
                .eq(User::getId, user.getId());
        if (user.getPasswordCode() != null) {
            wrapper.set(User::getPasswordCode, passwordEncoder.encode(user.getPasswordCode()));
        }
        wrapper.set(user.getEnabled() != null, User::getEnabled, user.getEnabled());
        this.update(wrapper);
    }

    /**
     * 重置密码
     *
     * @param account 账号
     * @param code    验证码
     */
    @Override
    @InitUpdateInfo
    public void resetPassword(String account, String code) {
        String defPassword = "123456789";
        String rcode = stringRedisTemplate.opsForValue().get(RedisConstants.EMAIL_VERIFICATION + account);
        if (!rcode.equals(code)) {
            throw new AppException(ResponseEnum.INCORRECT_VERIFICATION_CODE);
        }
        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<User>().eq(User::getAccount, account);
        wrapper.set(User::getPasswordCode, passwordEncoder.encode(defPassword));
        emailService.send(account, defPassword);
        this.update(wrapper);
    }

    /**
     * 修改密码
     *
     * @param userUpPassVO
     */
    @Override
    public void updatePassword(UserUpPassVO userUpPassVO) {
        Long userId = JwtUtils.getUserId();
        User user = userMapper.selectById(userId);
        String passwordCode = user.getPasswordCode();
        if (!passwordEncoder.matches(userUpPassVO.getPassword(), passwordCode)) { // 比较用户密码是否正确
            throw new AppException(ResponseEnum.PASSWORD_ERROR);
        }
        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<User>().eq(User::getAccount, JwtUtils.getUserAccount());
        wrapper.set(User::getPasswordCode, passwordEncoder.encode(userUpPassVO.getNewPassword()));
        emailService.send(JwtUtils.getUserAccount(), userUpPassVO.getNewPassword());
        this.update(wrapper);
    }

    /**
     * 流程图用户注册
     *
     * @param user
     * @return
     */
    @Override
    public String registerFromFlow(RegisterUserVO user) {
        if (StrUtil.isEmpty(user.getEmailVerificationCode())) {
            throw new AppException(ResponseEnum.VERIFICATION_CODE_CANNOT_BE_EMPTY);
        }
        // 判断验证码是否存在
        String rcode = stringRedisTemplate.opsForValue().get(RedisConstants.EMAIL_VERIFICATION + user.getAccount());
        if (!rcode.equals(user.getEmailVerificationCode())) {
            throw new AppException(ResponseEnum.INCORRECT_VERIFICATION_CODE);
        }
        user.setCompanyId(0L);
        user.setEnabled(1);
        user.setPasswordCode(passwordEncoder.encode(user.getPasswordCode()));
        User udb = userMapper.getByAccount(user.getAccount());
        if (ObjectUtil.isNotEmpty(udb)) {
            throw new AppException(ResponseEnum.EXIST_ACCOUNT);
        }
        userMapper.insert(user);
        userRoleService.insertInitFlowRole(user.getId());
        filePathClient.initFilePathData(user.getAccount());
        return StrUtil.isEmpty(user.getUsername()) ? user.getAccount() : user.getUsername();
    }
}
